<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Composer;
use Config;

class MakeRepository extends Command {
  /**
   * The name and signature of the console command.
   *
   * @var string
   */
  protected $signature = 'make:repository
                             {name : repository name}
                             {--model= : model name}';

  /**
   * The console command description.
   *
   * @var string
   */
  protected $description = 'create a new repository';


  protected $files;

  protected $composer;

  protected $model;

  protected $repository;

  /**
   * Create a new command instance.
   *
   * @return void
   */
  public function __construct( Filesystem $filesystem, Composer $composer ) {
    parent::__construct();

    $this->files    = $filesystem;
    $this->composer = $composer;
  }

  /**
   * Execute the console command.
   *
   * @return mixed
   */
  public function handle() {
    $repositoryName = $this->argument( 'name' );
    $modelName      = $this->option( 'model' );
    $this->setRepository( $repositoryName );
    $this->setModel( $modelName );
    $this->writeRepositoryAndInterface();
    $this->writeRepositoryService( );
    //$this->composer->dumpAutoloads();
  }

  private function writeRepositoryService() {
    $directory = $this->getDirectory();
    $files = $this->files->files($directory);
    $strData="\n    ";
    if(count($files)<0){
      return false;
    }
    foreach ( $files as $file ) {
      $className =basename($file);
      $className = substr($className,0,strlen($className)-4) ;
      $classSpaceName = Config::get( 'repository.repository_namespace' )."\\".$className;
      $interfaceName = Config::get( 'repository.repository_interface_namespace' )."\\".$className."Interface";
      $strBind = sprintf('$this->app->bind(\'%s\',\'%s\');',$interfaceName,$classSpaceName);
      $strData=$strData.$strBind."\n    ";
    }
    $template = $this->files->get( __DIR__ . DIRECTORY_SEPARATOR . 'stub' . DIRECTORY_SEPARATOR . 'ServiceProvider.stub' );
    $template = $this->getRenderStub(['all_class'=>$strData],$template);
    $providers_path = Config::get( 'repository.providers_path' );
    $this->files->put( $providers_path, $template );
  }

  public function writeRepositoryAndInterface() {
    $repositoryName = $this->getRepository();
    // 创建文件存放路径, RepositoryInterface放在app/Repositories,Repository个人一般放在app/Repositories/Eloquent里
    $directory = $this->getDirectory();

    //检查路径是否存在,不存在创建一个,并赋予775权限
    if ( ! $this->files->isDirectory( $directory ) ) {
      $this->files->makeDirectory( $directory, 0755, true );
    }
    //渲染模板文件,替换模板文件中变量值
    $templates = $this->templateStub();
    $class     = null;
    foreach ( $templates as $key => $template ) {
      //根据不同路径,渲染对应的模板文件
      if ( ! $this->files->exists( $this->getPath( $key ) ) ) {
        $class = $this->files->put( $this->getPath( $key ), $template );
      }
    }

    if ( $class) {
      //若生成成功,则输出信息
      $this->info( 'Success to make a ' . ucfirst( $repositoryName ) . ' Repository and a ' . ucfirst( $repositoryName ) . 'Interface Interface' );
    }else{
      $this->error( 'fail to make a ' . ucfirst( $repositoryName ) . ' Repository and a ' . ucfirst( $repositoryName ) . 'Interface Interface' );
    }

  }


  private function templateStub() {
    // 获取两个模板文件
    $stubs = [
      'Repository' => $this->files->get( __DIR__ . DIRECTORY_SEPARATOR . 'stub' . DIRECTORY_SEPARATOR . 'Repository.stub' ),
      'Interface'  => $this->files->get( __DIR__ . DIRECTORY_SEPARATOR . 'stub' . DIRECTORY_SEPARATOR . 'RepositoryInterface.stub' ),
    ];
    // 获取需要替换的模板文件中变量
    $templateData = $this->getTemplateData();

    $renderStubs = [];
    foreach ( $stubs as $key => $stub ) {
      // 进行模板渲染
      $renderStubs[ $key ] = $this->getRenderStub( $templateData, $stub );
    }

    return $renderStubs;
  }


  private function getPath( $class ) {
    // 两个模板文件,对应的两个路径
    $path = null;
    switch ( $class ) {
      case 'Repository':
        $path = $this->getDirectory() . DIRECTORY_SEPARATOR . $this->getRepositoryName() . '.php';
        break;
      case 'Interface':
        $path = $this->getInterfaceDirectory() . DIRECTORY_SEPARATOR . $this->getInterfaceName() . '.php';
        break;
    }

    return $path;
  }

  private function getTemplateData() {
    $repositoryNamespace          = Config::get( 'repository.repository_namespace' );
    $modelNamespace               = 'App\Models\\' . $this->getModelName();
    $repositoryInterfaceNamespace = Config::get( 'repository.repository_interface_namespace' );
    $repositoryInterface          = $this->getInterfaceName();
    $className                    = $this->getRepositoryName();
    $modelName                    = $this->getModelName();

    $templateVar = [
      'repository_namespace'           => $repositoryNamespace,
      'model_namespace'                => $modelNamespace,
      'repository_interface_namespace' => $repositoryInterfaceNamespace,
      'repository_interface'           => $repositoryInterface,
      'class_name'                     => $className,
      'model_name'                     => $modelName,
      'model_var_name'                 => strtolower( $modelName ),
    ];

    return $templateVar;
  }

  private function getModelName() {
    $modelName = $this->getModel();
    if ( isset( $modelName ) && ! empty( $modelName ) ) {
      $modelName = ucfirst( $modelName );
    } else {
      // 若option选项没写,则根据repository来生成Model Name
      $modelName = $this->getModelFromRepository();
    }

    return $modelName;
  }

  private function getDirectory() {
    return Config::get( 'repository.directory_path' );
  }

  /**
   * @param mixed $model
   */
  public function setModel( $model ) {
    $this->model = $model;
  }

  /**
   * @return mixed
   */
  public function getModel() {
    return $this->model;
  }

  private function getModelFromRepository() {
    $repository = strtolower( $this->getRepository() );
    $repository = str_replace( 'repository', '', $repository );

    return ucfirst( $repository );
  }

  /**
   * @return mixed
   */
  public function getRepository() {
    return $this->repository;
  }

  /**
   * @param mixed $repository
   */
  public function setRepository( $repositoryName ) {
    $this->repository = $repositoryName;
  }

  private function getInterfaceName() {
    return $this->getRepositoryName() . 'Interface';
  }

  private function getRepositoryName() {
    // 根据输入的repository变量参数,是否需要加上'Repository'
    $repositoryName = $this->getRepository();
    if ( ( strlen( $repositoryName ) < strlen( 'Repository' ) ) || strrpos( $repositoryName, 'Repository', - 11 ) ) {
      $repositoryName .= 'Repository';
    }

    return $repositoryName;
  }

  private function getRenderStub( $templateData, $stub ) {
    foreach ( $templateData as $search => $replace ) {
      $stub = str_replace( '$' . $search, $replace, $stub );
    }

    return $stub;
  }

  private function getInterfaceDirectory() {
    return Config::get( 'repository.directory_interface_path' );
  }


}
